import { _decorator, Component, Enum, EventHandler, EventTouch, Node, ScrollView, Size, UITransform, v3, Vec3 } from 'cc';
import { ListRender } from './ListRender';
import { PoolFactory } from './PoolFactory';
import { Task } from '../../Util/CustomTask';
const { ccclass, property, menu } = _decorator;

/**列表排列方式 */
enum ListType {
  /**水平排列 */
  Horizontal,
  /**垂直排列 */
  Vertical,
}

/**列表对齐方式 垂直方向 */
enum AlginVerType {
  /** 上对齐 */
  Top,
  /** 下对齐 */
  Bottom,
  /**  居中 */
  Center,
}

/**列表对齐方式 水平方向 */
enum AlginHorType {
  /** 左对齐 */
  Left,
  /** 右对齐 */
  Right,
  /**  居中 */
  Center,
}
/**
 * 列表
 * 根据cocos_example的listView改动而来
 * @author
 * @example
 *  1.创建cocos的ScrollView组件，添加List，设置List属性即可
 *
 */
@ccclass('List')
@menu('myComponent/List')
export class List extends Component {
  //==================== 属性面板 =========================
  /**对象池 */
  @property({ type: PoolFactory, tooltip: '对象池' })
  public pool: PoolFactory = null;

  @property({ tooltip: '列表项预制体' })
  public itemName: string = '';

  /**排列方式 */
  @property({ type: Enum(ListType), tooltip: '排列方式' })
  public type: ListType = ListType.Vertical;

  /**水平对齐方式 */
  @property({ type: Enum(AlginHorType), tooltip: '水平对齐方式\n上下滑动时控制整个滑动节点\n左右滑动时控制里面的item排列' })
  public alignHorType: AlginHorType = AlginHorType.Center;
  /**垂直对齐方式 */
  @property({ type: Enum(AlginVerType), tooltip: '垂直对齐方式\n上下滑动时里面的item排列\n左右滑动时控制整个滑动节点' })
  public alignVerType: AlginVerType = AlginVerType.Center;

  /**列表项之间X间隔 */
  @property({
    tooltip: '列表项X间隔',
  })
  public spaceX: number = 0;

  /**列表项之间Y间隔 */
  @property({
    tooltip: '列表项Y间隔',
  })
  public spaceY: number = 0;

  /**上间距 */
  @property({
    tooltip: '上间距',
  })
  public padding_top: number = 0;

  /**下间距 */
  @property({
    tooltip: '下间距',
  })
  public padding_bottom: number = 0;

  /**左间距 */
  @property({
    tooltip: '左间距',
  })
  public padding_left: number = 0;

  /**右间距 */
  @property({
    tooltip: '右间距',
  })
  public padding_right: number = 0;

  @property({
    type: EventHandler,
    tooltip: '点击事件',
  })
  clickEvent: EventHandler = new EventHandler();

  //====================== 滚动容器 ===============================
  /**列表滚动容器 */
  public scrollView: ScrollView = null;
  /**可活动范围 */
  protected contentParent: Size = null;
  /**scrollView的内容容器 */
  protected content: Node = null;
  /**scrollView的内容容器 */
  protected contentForm: UITransform = null;

  //======================== 列表项 ===========================
  /**列表项数据*/
  protected itemDataList: Array<unknown> = [];
  /**上次实例的数量 */
  protected tempItemNum: number = 0;
  /**应创建的实例数量 */
  public spawnCount: number = 0;
  /**存放列表项实例的数组 */
  protected itemList: Array<Node> = [];
  /**item的高度 */
  protected itemHeight: number = 0;
  /**item的宽度 */
  protected itemWidth: number = 0;
  /**item由于锚点不同的偏移 */
  protected moveX: number = 0;
  /**item由于锚点不同的偏移 */
  protected moveY: number = 0;

  //====================== 输入输出 ===============================
  // public inputFun: (data, ...param) => boolean = null;
  // public outputFun: (...param) => unknown = null;

  //======================= 计算参数 ==========================
  /** 现在的第一条数据下标 */
  protected nowContentPos: number = 0;
  /**网格行数 */
  protected gridRow: number = 0;
  /**网格列数 */
  protected gridCol: number = 0;
  /**可以看到的item数 */
  protected seeNum: number = 0;
  /**可以看到的行/列数 */
  protected spawnRCCount: number = 0;

  static ListType = ListType;
  static AlginVerType = AlginVerType;
  static AlginHorType = AlginHorType;

  protected onLoad() {
    this.scrollView = this.node.getComponent(ScrollView);
    if (this.type == ListType.Vertical) {
      this.scrollView.horizontal = false;
      this.scrollView.vertical = true;
    } else if (this.type == ListType.Horizontal) {
      this.scrollView.horizontal = true;
      this.scrollView.vertical = false;
    }
    const event = new EventHandler();
    event.target = this.node;
    event.component = 'List';
    event.handler = 'onScrolling';
    event.customEventData = '';
    this.scrollView.scrollEvents.push(event);

    this.content = this.scrollView.content;
    this.content.removeAllChildren();
    if (this.clickEvent.target != null) {
      this.content.on(Node.EventType.TOUCH_END, this.onClick, this);
    }
    this.contentForm = this.content.getComponent(UITransform);
    this.contentForm.setAnchorPoint(0, 1);

    const contentParent = this.content.parent.getComponent(UITransform);
    const form = this.node.getComponent(UITransform);
    // let ww = form.width - this.padding_left - this.padding_right;
    // if (ww <= 0) ww = form.width;
    // let hh = form.height - this.padding_top - this.padding_bottom;
    // if (hh <= 0) hh = form.height;
    this.contentParent = new Size(form.width, form.height);
    contentParent.setAnchorPoint(0.5, 0.5);
    this.content.parent.setPosition(0, 0);

    const itemRender = this.pool.pop(this.itemName);
    const itemForm = itemRender.getComponent(UITransform);
    this.itemHeight = itemForm.height;
    this.itemWidth = itemForm.width;
    this.moveX = itemForm.width * itemForm.anchorX;
    this.moveY = itemForm.height * itemForm.anchorY - itemForm.height;
    this.pool.push(itemRender);
    //初始化参数
    this.resetListParam();
  }

  /** 初始化参数 */
  resetListParam() {
    if (this.type == ListType.Vertical) {
      this.gridCol = Math.floor((this.contentParent.width - this.padding_left - this.padding_right + this.spaceX) / (this.itemWidth + this.spaceX));
      if (this.gridCol < 1) this.gridCol = 1;
      this.gridRow = Math.ceil((this.contentParent.height - this.padding_top - this.padding_bottom + this.spaceY) / (this.itemHeight + this.spaceY));
      if (this.gridRow < 1) this.gridRow = 1;
      this.spawnRCCount = this.gridRow + 1;
      this.spawnCount = this.gridCol * this.spawnRCCount;
      this.contentForm.width = this.gridCol * (this.itemWidth + this.spaceX) - this.spaceX;
    } else {
      this.gridRow = Math.floor((this.contentParent.height - this.padding_top - this.padding_bottom + this.spaceY) / (this.itemHeight + this.spaceY));
      if (this.gridRow < 1) this.gridRow = 1;
      this.gridCol = Math.ceil((this.contentParent.width - this.padding_left - this.padding_right + this.spaceX) / (this.itemWidth + this.spaceX));
      if (this.gridCol < 1) this.gridCol = 1;
      this.spawnRCCount = this.gridCol + 1;
      this.spawnCount = this.gridRow * this.spawnRCCount;
      this.contentForm.height = this.gridRow * (this.itemHeight + this.spaceY) - this.spaceY;
    }
    this.seeNum = this.gridCol * this.gridRow;
  }

  /**计算列表的各项参数 */
  protected countListParam() {
    const dataLen = this.itemDataList.length;
    if (dataLen <= 0) return;
    this.scrollView.stopAutoScroll();

    //重新计算滑动节点的变化部分的大小
    if (this.type == ListType.Horizontal) {
      this.contentForm.width = Math.ceil(dataLen / this.gridRow) * (this.itemWidth + this.spaceX) - this.spaceX + this.padding_left + this.padding_right;
      this.scrollView.enabled = this.contentForm.width > this.contentParent.width;
    } else {
      this.contentForm.height = Math.ceil(dataLen / this.gridCol) * (this.itemHeight + this.spaceY) - this.spaceY + this.padding_top + this.padding_bottom;
      this.scrollView.enabled = this.contentForm.height > this.contentParent.height + this.padding_top + this.padding_bottom;
    }
    this.resetContentPos();
    this.tempItemNum = dataLen;
  }

  protected resetContentPos() {
    const dataLen = this.itemDataList.length;
    //记录一下是什么方向移动
    const boo = this.type == ListType.Horizontal;
    //内容变化要稍微摆正滑动的节点
    const pos = this.content.getPosition();
    if (!this.scrollView.enabled) {
      //内容一眼看完
      if (boo) {
        if (this.alignHorType == AlginHorType.Left) {
          pos.x = -this.contentParent.width / 2;
        } else if (this.alignHorType == AlginHorType.Right) {
          pos.x = this.contentParent.width / 2 - this.contentForm.width + this.padding_right;
        } else {
          pos.x = -this.contentForm.width / 2;
        }
      } else {
        if (this.alignVerType == AlginVerType.Top) {
          pos.y = this.contentParent.height / 2;
        } else if (this.alignVerType == AlginVerType.Bottom) {
          pos.y = this.contentForm.height - this.contentParent.height / 2 - this.padding_bottom;
        } else {
          pos.y = this.contentForm.height / 2;
        }
      }
    } else if (dataLen < this.tempItemNum) {
      if (boo) {
        if (pos.x < this.contentParent.width / 2 - this.contentForm.width) {
          pos.x = this.contentParent.width / 2 - this.contentForm.width;
        }
      } else {
        if (pos.y > this.contentForm.height - this.contentParent.height / 2) {
          pos.y = this.contentForm.height - this.contentParent.height / 2;
        }
      }
    }
    if (boo) {
      const temp = dataLen > this.gridRow ? this.gridRow : dataLen;
      if (this.alignVerType == AlginVerType.Top) {
        pos.y = this.contentParent.height / 2 - this.padding_top;
      } else if (this.alignVerType == AlginVerType.Bottom) {
        pos.y = (this.itemHeight + this.spaceY) * temp - this.spaceY - this.contentParent.height / 2 + this.padding_bottom;
      } else {
        pos.y = ((this.itemHeight + this.spaceY) * temp - this.spaceY) / 2;
      }
    } else {
      const temp = dataLen > this.gridCol ? this.gridCol : dataLen;
      if (this.alignHorType == AlginHorType.Left) {
        pos.x = -this.contentParent.width / 2 - this.padding_left;
      } else if (this.alignHorType == AlginHorType.Right) {
        pos.x = this.contentParent.width / 2 - ((this.itemWidth + this.spaceX) * temp - this.spaceX) + this.padding_right;
      } else {
        pos.x = -((this.itemWidth + this.spaceX) * temp - this.spaceX) / 2;
      }
    }
    this.content.setPosition(pos);
  }

  /**
   * 更新改变的item
   * @param begin   开始数
   * @param end     结束数
   */
  protected itemChangeV(begin: number, end: number) {
    const items = this.itemList;
    // const i = begin * this.gridCol;
    // const len = (end - begin) * this.gridCol + i;
    const dataLen = this.itemDataList.length;
    for (let j = begin; j < end; j++) {
      const temp = j % this.spawnCount;
      let item = items[temp];
      if (j >= dataLen) {
        if (item != null) item.active = false;
        continue;
      }
      if (item == null) {
        item = this.getItem();
        items[temp] = item;
        this.content.addChild(item);
      }
      const itemRender: ListRender = item.getComponent(ListRender);
      itemRender.itemIndex = j;
      itemRender.data = this.itemDataList[j];
      itemRender.dataChanged();
      item.setPosition(this.moveX + (j % this.gridCol) * (this.itemWidth + this.spaceX), this.moveY - Math.floor(j / this.gridCol) * (this.itemHeight + this.spaceY) - this.padding_top);
      item.active = true;
    }
  }

  /**
   * 更新改变的item
   * @param begin     开始数
   * @param end       结束数
   */
  protected itemChangeH(begin: number, end: number) {
    const items = this.itemList;
    // const i = begin * this.gridCol;
    // const len = (end - begin) * this.gridCol + i;
    const dataLen = this.itemDataList.length;
    for (let j = begin; j < end; j++) {
      const temp = j % this.spawnCount;
      let item = items[temp];
      if (j >= dataLen) {
        if (item != null) item.active = false;
        continue;
      }
      if (item == null) {
        item = this.getItem();
        items[temp] = item;
        this.content.addChild(item);
      }
      const itemRender: ListRender = item.getComponent(ListRender);
      itemRender.itemIndex = j;
      itemRender.data = this.itemDataList[j];
      itemRender.dataChanged();
      item.setPosition(this.padding_left + this.moveX + Math.floor(j / this.gridRow) * (this.itemWidth + this.spaceX), this.moveY - (j % this.gridRow) * (this.itemHeight + this.spaceY));
      item.active = true;
    }
  }

  public updateItem(begin: number, end: number) {
    if (begin >= 0 && begin >= end) return;
    if (this.type == ListType.Vertical) {
      this.itemChangeV(begin, end);
    } else {
      this.itemChangeH(begin, end);
    }
  }

  updateContentItem() {
    this.content.children.forEach((child) => {
      const itemRender: ListRender = child.getComponent(ListRender);
      itemRender.dataChanged();
    });
  }

  protected onScrolling() {
    // this.bScrolling = true;
    if (this.itemDataList.length <= this.seeNum) return;
    const nowPos = this.content.getPosition();
    if (this.type == ListType.Vertical) {
      // this.contentParent.height / 2 ~ this.contentForm.height - this.contentParent.height / 2
      if (this.contentParent.height / 2 <= nowPos.y && this.contentForm.height - this.contentParent.height / 2 >= nowPos.y) {
        const posY = nowPos.y - this.contentParent.height / 2;
        const temp = Math.floor(posY / (this.itemHeight + this.spaceY));
        if (temp > this.nowContentPos) {
          const moveNum = temp - this.nowContentPos;
          if (moveNum > this.spawnRCCount) {
            this.updateItem(temp * this.gridCol, (temp + this.spawnRCCount) * this.gridCol);
          } else {
            this.updateItem((this.nowContentPos + this.spawnRCCount) * this.gridCol, (this.nowContentPos + this.spawnRCCount + moveNum) * this.gridCol);
          }
          this.nowContentPos = temp;
        } else if (temp < this.nowContentPos) {
          const moveNum = this.nowContentPos - temp;
          // const begin = temp * this.gridCol;
          if (moveNum > this.spawnRCCount) {
            this.updateItem(temp * this.gridCol, (temp + this.spawnRCCount) * this.gridCol);
          } else {
            this.updateItem(temp * this.gridCol, (temp + moveNum) * this.gridCol);
          }
          this.nowContentPos = temp;
        }
        this.scrollCallback && this.scrollCallback(this.nowContentPos);
      }
    } else {
      // -this.contentForm.width + this.contentParent.width / 2 ~ -this.contentParent.width / 2
      if (-this.contentForm.width + this.contentParent.width / 2 <= nowPos.x && -this.contentParent.width / 2 >= nowPos.x) {
        const posX = nowPos.x + this.contentParent.width / 2;
        // console.log('posX: ' + posX);
        const temp = Math.floor(-posX / (this.itemWidth + this.spaceX));
        if (temp > this.nowContentPos) {
          const moveNum = temp - this.nowContentPos;
          if (moveNum > this.spawnRCCount) {
            this.updateItem(temp * this.gridRow, (temp + this.spawnRCCount) * this.gridRow);
          } else {
            this.updateItem((this.nowContentPos + this.spawnRCCount) * this.gridRow, (this.nowContentPos + this.spawnRCCount + moveNum) * this.gridRow);
          }
          this.nowContentPos = temp;
          // console.log('1tooooo: ' + temp + ' ' + moveNum);
        } else if (temp < this.nowContentPos) {
          const moveNum = this.nowContentPos - temp;
          if (moveNum > this.spawnRCCount) {
            this.updateItem(temp * this.gridRow, (temp + this.spawnRCCount) * this.gridRow);
          } else {
            this.updateItem(temp * this.gridRow, (temp + moveNum) * this.gridRow);
          }
          this.nowContentPos = temp;
          // console.log('2tooooo: ' + temp + ' ' + moveNum);
        }
      }
    }
  }

  private scrollCallback: Task.next = null;
  setScrollCallback(next: Task.next) {
    this.scrollCallback = next;
  }

  refreshContent() {
    this.onScrolling();
  }

  /**
   * 列表数据 (列表数据复制使用，如果列表数据改变，则需要重新设置一遍数据)
   * @param itemDataList item数据列表
   */
  public setData(itemDataList: Array<unknown>) {
    this.itemDataList = itemDataList;
    if (this.node.active) {
      this.countListParam();
      // this.resetContentPos();
      this.updateItem(0, this.spawnCount);
      this.startDoSomething();
    }
  }

  /**获取一个列表项 */
  protected getItem() {
    return this.pool.pop(this.itemName);
  }

  /**获取item在scrollView的局部坐标 */
  public getPositionInView(index: number): Vec3 {
    if (index < 0 || index >= this.itemDataList.length) return v3(0, 0);
    if (this.type == ListType.Vertical) {
      return v3((index % this.gridCol) * (this.itemWidth + this.spaceX), -Math.floor(index / this.gridCol) * (this.itemHeight + this.spaceY));
    } else {
      return v3((index % this.gridRow) * (this.itemWidth + this.spaceX), -Math.floor(index / this.gridRow) * (this.itemHeight + this.spaceY));
    }
  }

  protected onClick(event: EventTouch) {
    const clickPos = event.getUILocation();
    const pos = this.contentForm.convertToNodeSpaceAR(v3(clickPos.x, clickPos.y));
    if (-pos.y % (this.itemHeight + this.spaceY) > this.itemHeight) return;
    if (pos.x % (this.itemWidth + this.spaceX) > this.itemWidth) return;
    const row = Math.floor(-pos.y / (this.itemHeight + this.spaceY));
    const col = Math.floor(pos.x / (this.itemWidth + this.spaceX));
    let i = 0;
    if (this.type == ListType.Vertical) {
      i = this.gridCol * row + col;
    } else {
      i = this.gridRow * col + row;
    }
    if (i < 0 || this.itemDataList == null || this.itemDataList.length <= i) return;
    this.clickEvent.emit([this.itemList[i % this.spawnCount], this.clickEvent.customEventData]);
  }

  /**获取列表数据 */
  public getListData() {
    return this.itemDataList;
  }

  /**
   * 增加一项数据到列表的末尾
   * @param data 数据
   */
  public addItem(data: unknown) {
    this.itemDataList.push(data);
    const end = this.itemDataList.length;
    if (this.type == ListType.Vertical) {
      if (this.nowContentPos * this.gridCol + this.spawnCount < end) return;
    } else {
      if (this.nowContentPos * this.gridRow + this.spawnCount < end) return;
    }
    this.countListParam();
    this.updateItem(end - 1, end);
  }

  /**
   * 增加一项数据到列表指定位置
   * @param index   位置，0表示第1项
   * @param data  数据
   */
  public addItemAt(index: number, data: unknown) {
    if (index >= this.itemDataList.length) {
      this.addItem(data);
      return;
    }
    this.itemDataList.splice(index, 1, data);
    let begin;
    if (this.type == ListType.Vertical) {
      begin = this.nowContentPos * this.gridCol;
    } else {
      begin = this.nowContentPos * this.gridRow;
    }
    const end = begin + this.spawnCount;
    if (index > end) return;
    begin = index;
    this.countListParam();
    this.updateItem(begin, end);
  }

  /**
   * 删除一项数据
   * @param index 删除项的位置 ,0表示第1项
   */
  public deleteItem(index: number) {
    if (index >= this.itemDataList.length) return;
    this.itemDataList.splice(index, 1);
    let begin;
    if (this.type == ListType.Vertical) {
      begin = this.nowContentPos * this.gridCol;
    } else {
      begin = this.nowContentPos * this.gridRow;
    }
    const end = begin + this.spawnCount;
    if (index > end) return;
    begin = index;
    this.countListParam();
    this.updateItem(index, end);
  }

  /**
   * 改变一项数据
   * @param index   位置,0表示第1项
   * @param data  替换的数据
   */
  public changeItem(index: number, data: unknown) {
    if (index >= this.itemDataList.length) return;
    this.itemDataList[index] = data;
    this.updateItem(index, index + 1);
  }

  /**
   * 移动到。。
   * @param index   位置,0表示第1项
   */
  public moveTo(index: number) {
    if (index >= this.itemDataList.length) return;
    const pos = this.content.getPosition();
    if (this.type == ListType.Vertical) {
      pos.y = Math.floor(index / this.gridCol) * (this.itemHeight + this.spaceY);
      this.content.setPosition(pos);
    } else {
      pos.x = Math.floor(index / this.gridRow) * (this.itemWidth + this.spaceX);
      this.content.setPosition(pos);
    }
  }

  /**
   * 改变一段数据
   * @param index   位置,0表示第1项
   * @param data  替换的数据
   */
  public changeData(data: unknown[]) {
    this.itemDataList = data;
    this.countListParam();
    this.updateItem(this.nowContentPos, this.nowContentPos + this.spawnCount);
  }

  public getSeeScriptList<T extends ListRender>(begin: number = 0, end?: number): T[] {
    let nowBegin;
    if (this.type == ListType.Vertical) {
      nowBegin = this.nowContentPos * this.gridCol;
    } else {
      nowBegin = this.nowContentPos * this.gridRow;
    }
    const nowEnd = nowBegin + this.seeNum;
    if (begin < nowBegin || begin > nowEnd) return [];
    if (end == null || end > nowEnd) end = nowEnd;
    const result = [];
    for (; begin < end; begin++) {
      const temp = begin % this.spawnCount;
      const item = this.itemList[temp];
      if (!item) continue;
      const sp = item.getComponent(ListRender);
      if (!sp) continue;
      result.push(sp as T);
    }
    return result;
  }

  overStartDoing: (count: number) => void = null;

  /** 初始化完成之后可能会有一写动画效果 */
  public startDoSomething() {
    const items = this.itemList;
    const dataLen = this.itemDataList.length;
    const end = this.seeNum;
    let j = 0;
    for (; j < end && j < dataLen; j++) {
      const temp = j % this.spawnCount;
      const item = items[temp];
      if (item == null) {
        break;
      }
      const itemRender: ListRender = item.getComponent(ListRender);
      itemRender.onEnableDoing(j);
    }
    if (this.overStartDoing != null) {
      this.overStartDoing(j);
    }
  }

  public onDisable() {
    this.itemList.forEach((ele) => {
      this.pool.push(ele);
    });
    this.itemList.length = 0;
    this.nowContentPos = 0;
    if (this.type == ListType.Vertical) {
      this.scrollView.scrollToTop();
    } else {
      this.scrollView.scrollToLeft();
    }
    //清理列表数据
    this.itemDataList = [];
  }
}
